#include <iostream>
#include <vector>
#include "pthread.hpp"
#include "lockguard.hpp"

using namespace mypthread;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //全局的方式定义一把锁

// class ThreadData //封装的目的是我想在进程抢票的时候知道谁在抢票，最后抢了多少张票
// {
// public:
//     ThreadData(string name, int &tickets) : _name(name), _tickets(tickets), _total(0)
//     {
//     }

// public:
//     int &_tickets; // 引用是为了让所有线程看到同一个空间
//     string _name;  // 线程名
//     int _total;    // 线程抢票的总数
// };

//加锁之后的版本
class ThreadData
{
public:
    ThreadData(string name, int &tickets, pthread_mutex_t& mutex) : _name(name), _tickets(tickets), _mutex(mutex), _total(0)
    {
    }

public:
    int &_tickets; 
    string _name;  
    int _total;    
    pthread_mutex_t& _mutex;//所有线程竞争一把锁 
};

void routine(int &n)
{
    while (n--)
    {
        cout << "i am a new thread, " << n << endl;
        sleep(1);
    }
}

// void obtainTickets(ThreadData *ptd)
// {
//     // 这种抢票会出现一个问题，票出现了负数，因为共享资源没有保护
//     while (true)
//     {
//         if (ptd->_tickets > 0)
//         {
//             usleep(1000);
//             cout << ptd->_name << ", tickets number is " << ptd->_tickets << endl;
//             ptd->_tickets--;
//             ptd->_total++;
//         }
//         else
//         {
//             break;
//         }
//     }
// }


// void obtainTickets(ThreadData *ptd)
// {
//     // 加锁保护临界区，本质是将并行访问变成串行访问，加锁的粒度越细越好
//     // 加锁之后每个线程会自由竞争锁，但是有的线程竞争能力强，有的弱，会出现恶意竞争，这就是饥饿问题
//     while (true)
//     {
//         pthread_mutex_lock(&ptd->_mutex);
//         if (ptd->_tickets > 0)
//         {
//             usleep(1000);
//             cout << ptd->_name << ", tickets number is " << ptd->_tickets << endl;
//             ptd->_tickets--;
//             ptd->_total++;
//             pthread_mutex_unlock(&ptd->_mutex); //这种加锁和解锁有个缺点，就是可以遗忘解锁或者解锁地方不对，所以采用RAII风格的加锁和解锁
//         }
//         else
//         {
//             pthread_mutex_unlock(&ptd->_mutex);
//             break;
//         }
//     }
// }


//RAII风格的加锁解锁 
void obtainTickets(ThreadData *ptd)
{
    while (true)
    {
        LockGuard lockguard(mutex);
        if (ptd->_tickets > 0)
        {
            usleep(1000);
            cout << ptd->_name << ", tickets number is " << ptd->_tickets << endl;
            ptd->_tickets--;
            ptd->_total++;
        }
        else
        {
            break;
        }
    }
}

int main()
{
    vector<Thread<ThreadData*>> threads;
    vector<ThreadData*> threaddatas;
    int tickets = 10000;
    //创建一批线程去抢票
    for(int i = 0; i < 5; i++)
    {
        string name = "thread-" + to_string(i+1);
        ThreadData* td = new ThreadData(name, tickets, mutex);
        threaddatas.emplace_back(td);
        threads.emplace_back(obtainTickets, td, name); 
        sleep(1);
        //这里有时候有个隐藏的风险，主线程运行的太快，当第一次循环结束之后，创建的线程还没来得及执行方法，
        //主线程已经进入二次循环，这时刚才最后一个元素可能就不是最后一个元素了，也许现在最后一个元素还没准备好，就被刚才线程用了，
        //此时就会错误
        //解决方法：睡眠几秒
    }


    //等待所有线程
    for(auto thread : threads)
    {
        thread.join();
    }


    //打印最终的抢票结果
    for(auto threaddata : threaddatas)
    {
        cout << threaddata->_name << " total is " << threaddata->_total << endl;
    }
}